import pyperclip
import random
import json
import string
from datetime import datetime
from typing import Dict

def get_current_datetime() -> str:
    return datetime.now().strftime("%Y年%m月%d日 %H:%M:%S")

def get_unique_charset(s: str) -> str:
    return ''.join(set(s))

class PasswordGenerator():

    # 下述变量摘自 string 模块，punctuation 剔除了双引号 " 和反斜杠 \ ，避免转为 json 字符串时出现转义字符
    default_dictionary = dict(
        lowercase = string.ascii_lowercase,
        uppercase = string.ascii_uppercase,
        letters = string.ascii_letters,
        digits = '0123456789',
        punctuation = r"""!#$%&'()*+,-./:;<=>?@[]^_`{|}~"""
    )

    def __init__(self, dictionary: Dict[str, str]=default_dictionary):
        assert len(dictionary) > 0
        self.dictionary = dictionary
        self.charset = ""
        for k in dictionary.keys():
            assert len(dictionary[k]) > 0
            dictionary[k] = get_unique_charset(dictionary[k])
            self.charset += dictionary[k]
        self.charset = get_unique_charset(self.charset)

    def generate(self, length: int, min_num_required: Dict[str, int]={}) -> str:
        s = []
        for k, num in min_num_required.items():
            chars = self.dictionary[k]
            for c in chars:
                num -= s.count(c)
            if num <= 0:
                continue
            if num + len(s) > length:
                raise ValueError("min_num_required 中的数值之和大于密码长度。")
            s += random.choices(chars, k=num)
        s += random.choices(self.charset, k=length - len(s))
        random.shuffle(s)
        return ''.join(s)

if __name__ == '__main__':
    save_path = "passwd.json"

    # 生成一个长度为 16 的密码，要求至少含 1 个小写英文字母、1 个大写英文字母、1 个特殊字符和1 个数字
    pwd_generator = PasswordGenerator()
    passwd = pwd_generator.generate(
        length=16,
        min_num_required=dict(
            lowercase=1,
            uppercase=1,
            punctuation=1,
            digits=1
        )
    )

    info_to_save = {
        "url": "https://simpletex.cn/user/register",
        "account": "",
        "password": passwd,
        "generate_time": get_current_datetime(),
    }

    with open(save_path, 'a', encoding='utf-8') as f:
        json.dump(info_to_save, f, ensure_ascii=False, indent=4)
        f.write('\n')

    pyperclip.copy(passwd)
    print('密码已复制到剪贴板，快去粘贴吧~')
